package com.rraj.common.redis.dataRedis;

import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.rraj.common.redis.rediskey.RedisKeyFactory;
import com.rraj.common.redis.rediskey.StrRedisKey;
import com.rraj.common.utils.BeanCopyUtil;
import com.rraj.common.utils.StringUtil;
import com.xiaoleilu.hutool.date.DateUtil;
import com.xiaoleilu.hutool.util.CollectionUtil;
import com.xiaoleilu.hutool.util.NumberUtil;
import com.xiaoleilu.hutool.util.ObjectUtil;
import com.xiaoleilu.hutool.util.StrUtil;

/**
 * Created by WangSheng on 2017/11/8.
 *
 * @author fengli
 * @date 2017/07/18
 */
@Component
public class RedisClientUtil {
    @Autowired
    private StringRedisTemplate redisTemplate;

    /*************************************common 操作***************************************/
    /**
     * 删除操作
     * 该操作单个删除，如果需要对key进行格式化，请调用getFormatKey()方法
     * @param key
     */
    public void delete(String key){             
        if(StrUtil.isNotBlank(key)){
            redisTemplate.delete(key);
        }       
    }
    
    /**
     * 删除操作
     * 该操作批量删除，如果需要对key进行格式化，请调用getFormatBatchKey()方法
     * @param batchOriginKey
     */
    public void deleteBatch(Collection<String> batchOriginKey){
        if(CollectionUtil.isNotEmpty(batchOriginKey)){
            redisTemplate.delete(batchOriginKey);
        }
    }
    
    /** 
     * 按时间点设置过期时间
     * @param key
     * @param expireDate
     * @return      
     * boolean      
     */  
    public boolean expireToDate(String key, Date expireDate){
    	return expire(expireDate, null, key);
    }
    
    /** 
     * 按时间段设置过期时间
     * @param key
     * @param expireSecond
     * @return      
     * boolean      
     */  
    public boolean expireBySecond(String key, Long expireSecond){
    	return expire(null, expireSecond, key);
    }
    

    /**
     * 判断是否存在当前Key，如果需要对key进行格式化，请设置rediskey的keyFormat值
     * @param redisKey
     * @return
     */
    public boolean exists(String key){
        return redisTemplate.hasKey(key);
    }

    /**
     * 匹配所有符合条件的key
     * @param redisKey
     * @return
     */
    public Set<String> keys(String key){
        return redisTemplate.keys(key);
    }

    /*************************************str 操作******************************************/

    /** 
     * 字符串设置值
     * 带过期时间点设置值
     * @param key
     * @param value
     * @param expireDate  过期时间点    
     * void      
     */  
    public void set(String key,  String value, Date expireDate){
        if(ObjectUtil.isNotNull(expireDate)){
            redisTemplate.opsForValue().set(key, value, getExpirePeriod(expireDate, null), TimeUnit.MILLISECONDS);
        }else{
            redisTemplate.opsForValue().set(key, value, RedisConst.ExpireTime.FIVE_MINTES*1000, TimeUnit.MILLISECONDS);
        }
    }
       
    /** 
     * 字符串设置值
     * 带过期时间段设置值
     * @param key
     * @param value
     * @param expireSecond      
     * void      
     */  
    public void set(String key,  String value, Long expireSecond){
        if(ObjectUtil.isNotNull(expireSecond)){
            redisTemplate.opsForValue().set(key, value, getExpirePeriod(null, expireSecond), TimeUnit.MILLISECONDS);
        }else{
            redisTemplate.opsForValue().set(key, value, RedisConst.ExpireTime.FIVE_MINTES*1000, TimeUnit.MILLISECONDS);
        }
    }

    /**
     * 获取数据
     * 如果需要对key进行格式化，请设置rediskey的keyFormat值
     * @param key
     * @return
     */
    public String get(String key){
        return redisTemplate.opsForValue().get(key);
    }
    /**
     * 获取数据
     * @param keyFormat
     * @param keyValues
     * @return
     */
    public String getByFormatKey(String keyFormat, String[] keyValues){
        StrRedisKey redisKey = RedisKeyFactory.getStrRedisKey();
        redisKey.setKeyFormat(keyFormat);
        redisKey.setKeyValues(keyValues);
        return get(redisKey);
    }

    /**
     * 设置List对象
     * @param key
     * @param list
     * @param list
     */
    public void setList(String key, List list, Date date){
        String data = JSONObject.toJSONString(list);
        set(key, data, date);
    }
    
    /** 
     * 设置List对象
     * @param key
     * @param list
     * @param second 秒      
     * void      
     */  
    public void setList(String key, List list, Long second){
        String data = JSONObject.toJSONString(list);
        set(key, data, second);
    }

    /**
     * 设置Object对象
     * @param redisKey
     * @param obj
     */
    public void setObject(String key, Object obj, Date date){
        String data = JSONObject.toJSONString(obj);
        set(key, data, date);
    }
    
    /**
     * 设置Object对象
     * @param redisKey
     * @param obj
     */
    public void setObject(String key, Object obj, Long second){
    	String data = JSONObject.toJSONString(obj);
    	set(key, data, second);
    }

    /**
     * 获得List对象
     * @param redisKey
     * @param cls
     * @param <T>
     * @return
     */
    public <T> List<T> getList(String key, Class<T> cls){
        String data = get(key);
        if (StringUtil.isNotEmpty(data)) {
            return JSONObject.parseArray(data, cls);
        }
        return null;
    }

    /**
     * 获得Object对象
     * @param redisKey
     * @param cls
     * @param <T>
     * @return
     */
    public <T> T getObject(String key, Class<T> cls){
        String data = get(key);
        if (StringUtil.isNotEmpty(data)) {
            return JSONObject.parseObject(data, cls);
        }
        return null;
    }
    
    /**
     * 计数器，每次自增固定数值
     * @param redisKey
     * @return
     */
    public Long incrByLong(String key, String value, Object time) { 
    	if (null == time) {
    		if(NumberUtil.isInteger(value)){
    			Long index = redisTemplate.opsForValue().increment(key, Long.parseLong(value));
    			return index;
        	}
    	}
    	if (time instanceof Long) {
    		Long second = (Long) time;
    		if(NumberUtil.isInteger(value)){
    			Long index = redisTemplate.opsForValue().increment(key, Long.parseLong(value));
        		if (second != 0L && second != null) {   
        			this.expire(null, second, key);
        		}
        		return index;
        	}
    	} 
    	if (time instanceof Date) {
    		if(NumberUtil.isInteger(value)){
    			Date date = (Date) time;    
    			Long index = redisTemplate.opsForValue().increment(key, Long.parseLong(value));
        		if (date != null) {   			
        			this.expire(date, null, key);
        		}
        		return index;
        	}
    	} 
    	return null;
    }
    
    /**
     * 计数器，每次自增固定数值
     * @param redisKey
     * @return
     */
    public Long incrByLong(String key, String value) {
    	return incrByLong(key, value, null);
    }
    
    /**
     * 计数器，每次自增固定数值
     * @param redisKey
     * @return
     */
    public Double incrByDouble(String key, String value, Date date) {
        if(NumberUtil.isDouble(value)){
            Double index = redisTemplate.opsForValue().increment(key, Double.parseDouble(value));
            if (date != null) {   			
    			this.expire(date, null, key);
    		}
            return index;
        }
        return null;
    }
    
    /**
     * 计数器，每次自增固定数值
     * @param redisKey
     * @return
     */
    public Double incrByDouble(String key, String value, Long second) {
    	if(NumberUtil.isDouble(value)){
    		Double index = redisTemplate.opsForValue().increment(key, Double.parseDouble(value));
    		if (second != 0L && second != null) {   			
    			this.expire(null, second, key);
    		}
    		return index;
    	}
    	return null;
    }
    /**
     * 计数器，每次自增1
     * @param redisKey
     * @return
     */
    public Long incrBy(String key, Long second) {
        return this.incrByLong(key, "1", second);
    }
    
    /**
     * 计数器，每次自增1
     * @param redisKey
     * @return
     */
    public Long incrBy(String key, Date date) {
    	return this.incrByLong(key, "1", date);
    }
    
    
    /**
     * 计数器，每次自增1
     * @param redisKey
     * @return
     */
    public Long incrBy(String key) {
    	return this.incrByLong(key, "1", null);
    }

    /*************************************hash 操作*****************************************/
    /**
     * 设置对象属性值
     * @param redisKey
     */
    public void hset(String key, Map<String, String> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 获得对象属性值
     * @param redisKey
     * @return
     */
    public String hget(String key, String fieldKey){
        Object obj = redisTemplate.opsForHash().get(key, fieldKey);
        if(ObjectUtil.isNull(obj)){
            return null;
        }
        return obj.toString();
    }
    /**
     * 泛型获得对象属性值
     * @param redisKey
     * @return
     */
    public <T> T hget(String key, String fieldKey, Class<T> cls){
        String hget = hget(key, fieldKey);
        if(StrUtil.isBlank(hget)){
            return null;
        }
        return JSONObject.parseObject(hget, cls);
    }

    /**
     * 删除HashMap field字段
     * @param redisKey
     */
    public Long hdel(String key, String fieldKey) {
        return redisTemplate.opsForHash().delete(key, fieldKey);
    }

    /**
     * 将HashMap中的Key对应的Value加上一个数值
     * @param redisKey
     */
    public Long hIncrByLong(String key, String fieldKey, String value) {
        if(NumberUtil.isInteger(value)){
            return redisTemplate.opsForHash().increment(key, fieldKey, Long.parseLong(value.toString()));
        }
        return null;
    }
    /**
     * 将HashMap中的Key对应的Value加上一个Double值
     * @param redisKey
     */
    public Double hIncrByDouble(String key, String fieldKey, String value) {
        if(NumberUtil.isDouble(key)){
            return redisTemplate.opsForHash().increment(key, fieldKey, Double.parseDouble(value.toString()));
        }
        return null;
    }
    /**
     * 将HashMap中的Key对应的Value加上1
     * @param redisKey
     */
    public Long hIncrBy(String key, String fieldKey) {
        return hIncrByLong(key, fieldKey, "1");
    }

    public <T> T hgetObjetData(String key, Class<T> clas){
        Map<Object, Object> data = redisTemplate.opsForHash().entries(key);       
        if(data != null){
            return BeanCopyUtil.map(data, clas);
        }
        return null;
    }

    /*************************************list 操作*****************************************/
    
    /** 
     * 将一个值插入到列表头部
     * @param key
     * @param value
     * @return      
     * Long      
     */  
    public Long lpush(String key, String value){       
        return redisTemplate.opsForList().leftPush(key, value);   
    }
    /** 
     * 将多个值插入到列表头部
     * @param key
     * @param values
     * @return      
     * Long      
     */  
    public Long lpush(String key, Collection<String> values){   	   	
    	return redisTemplate.opsForList().leftPushAll(key, values);  	
    }

    /**
     * 通过key从list的头部删除一个value,并返回该value
     * @param redisKey
     * @return
     */
    public String lpop(String key){
        return redisTemplate.opsForList().leftPop(key);
    }
    /**
     * 泛型通过key从list的头部删除一个value,并返回该value
     * @param redisKey
     * @return
     */
    public <T> T lpop(String key, Class<T> cls){
        return JSONObject.parseObject(lpop(key), cls);
    }

    /*************************************set 操作******************************************/
    /**
     * 获取key下面所有值
     * @param redisKey
     * @return
     */
    public Set<String> smembers(String key){
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 查询key的数量
     * @param redisKey
     * @return
     */
    public Long scard(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 求所有key的并集
     * @param redisKey
     * @return
     */
    public Set sunion(Collection<String> batchOriginKey) {
        return redisTemplate.opsForSet().union("", batchOriginKey);
    }

    /**
     * 求redisKey1与redisKey2的差集
     * @param redisKey1
     * @param redisKey2
     * @return
     */
    public Set sdiff(String redisKey1, String redisKey2) {
        return redisTemplate.opsForSet().difference(redisKey1, redisKey2);
    }

    /**
     * 向集合添加元素
     * @param redisKey
     * @return
     */
    public Long sadd(String key, String value) {
        return redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 移除单个元素
     * @param redisKey
     * @return
     */
    public Long srem(String key, String value) {
        return redisTemplate.opsForSet().remove(key, value);
    }
    
    
    /**
     * 获取格式化后的key
     * @return
     */
    public String getFormatKey(String keyFormat, String...keyValues){
        return RedisClient.format(keyFormat, keyValues);
    }
    /**
     * 获取格式化后的批量key
     * @return
     */
    public Set<String> getFormatBatchKey(String keyFormat, Collection<String[]> batchKeyValues){
    	Set<String> keySet = new HashSet<>();
        for(String[] strs : batchKeyValues){
            String key = RedisClient.format(keyFormat, strs);
            keySet.add(key);
        }
        return keySet;
    }
    
    /** 
     * 设置过期时间
     * @param expireDate
     * @param expireSecond
     * @param key
     * @return      
     * boolean      
     */  
    private boolean expire(Date expireDate,Long expireSecond,String key){
        if(ObjectUtil.isNotNull(expireDate)){
            return redisTemplate.expireAt(key, expireDate);
        }else if(ObjectUtil.isNotNull(expireSecond)){
            return redisTemplate.expire(key, expireSecond, TimeUnit.SECONDS);
        }
        return false;
    }
    
    /**
     * 根据时间点获取过期时间段
     * @return 单位是毫秒，如果小于当前时间，则设置过期时间为-1
     */
    private Long getExpirePeriod(Date expireDate,Long expireSecond){
        if(ObjectUtil.isNotNull(expireSecond)){
            return expireSecond * 1000;
        }else if(ObjectUtil.isNotNull(expireDate)){
            long milliSecond =  expireDate.getTime() - DateUtil.current(false);
            if(milliSecond < 0){
                return 0L;
            }
            return milliSecond;
        }
        return 0L;
    }

    /**
     * 获取数据
     * 如果需要对key进行格式化，请设置rediskey的keyFormat值
     * @param redisKey
     * @return
     */
    private String get(StrRedisKey redisKey){
        return redisTemplate.opsForValue().get(redisKey.getDataBaseKey());
    }
    
}
